package com

import (
	"sync"
	"log"
	"strings"
	"time"
	"fmt"
	"errors"
)

type Scheduler struct {
	Nodes []*MacAddr `json:"nodes"`
}
type MacAddr struct {
	Mac string `json:"mac"`
}

type Nodes struct {
	lock sync.RWMutex
	Host map[string]NodeHost
}

type Mac struct {
	NodeCount int        `json:"nodeCount"`
	SdpCount  int        `json:"sdpCount"`
	Mac       string     `json:"mac"`
	Sdp       []NodeSdps `json:"sdp"`
}

type PullRespsonse struct {
	PeerId         string    `json:"peer_id"`
	Sdp            NodeOffer `json:"sdp"`
	OfferId        int64     `json:"offer_id"`
	Setup          int       `json:"setup"`
	RemoteOfferId  int64     `json:"remote_offer_id"`
	PredictionPort int       `json:"prediction_port"`
}

func (n *Nodes) GetNodeHost(to string) string {
	n.lock.RLock()
	res, ok := n.Host[strings.ToLower(string(to))];
	n.lock.RUnlock()
	if ok {
		host := res.Host
		return host
	}
	return ""
}

func (n *Nodes) GetPullNodeAnnounce(to []byte) *PullRespsonse {
	resp := new(PullRespsonse)
	n.lock.Lock()
	res, ok := n.Host[strings.ToLower(string(to))]
	if ok {
		if len(res.Announce.Sdps) < 1 {
			n.lock.Unlock()
			log.Printf("get node announce uid sdps len < 1:%v", string(to))
			return nil
		}
		//log.Printf("got node announce:%v", res.Announce.Sdps[0])

		resp.PredictionPort = res.Announce.PredictionPort
		resp.PeerId = res.PeerId
		resp.Sdp = NodeOffer{
			Sdp:  res.Announce.Sdps[0].Offer.Sdp,
			Type: res.Announce.Sdps[0].Offer.Type,
		}
		resp.OfferId = res.Announce.Sdps[0].OfferId
		resp.RemoteOfferId = res.Announce.Sdps[0].RemoteOfferId
		n.Host[string(to)].Announce.Sdps = n.Host[string(to)].Announce.Sdps[1:]

		n.lock.Unlock()
		return resp
	}
	n.lock.Unlock()
	return nil
}
func (n *Nodes) GetNodeAnnounce(to string) *NodeAnnounce {
	n.lock.RLock()
	//log.Printf("got node announce 1:%v", to)
	resp, ok := n.Host[strings.ToLower(to)]
	n.lock.RUnlock()
	if ok {
		//log.Printf("got node announce 2:%v", resp.Announce.Action)
		res := resp.Announce
		return res
	}
	return nil
}

func (n *Nodes) GetNodeMac(mac string) *Mac {
	res := new(Mac)
	if mac != "" {
		n.lock.RLock()
		log.Printf("got node announce 1:%v", mac)
		resp, ok := n.Host[strings.ToLower(mac)]
		n.lock.RUnlock()
		if ok {
			res.Mac = resp.Announce.PeerId
			res.Sdp = resp.Announce.Sdps
		}
	} else {
		n.lock.RLock()
		//defer n.lock.RUnlock()
		res.NodeCount = len(n.Host)
		var i int
		for _, host := range n.Host {
			if len(host.Announce.Sdps) < 1 {
				continue
			}
			i++
		}
		res.SdpCount = i
		n.lock.RUnlock()
	}
	return res
}

func (n *Nodes) DelNodeSdp(mac string) {
	if mac != "" {
		n.lock.Lock()
		//log.Printf("delete node sdp 1:%v", mac)
		_, ok := n.Host[strings.ToLower(mac)]
		if ok {
			if len(n.Host[strings.ToLower(mac)].Announce.Sdps) < 1 {
				n.lock.Unlock()
				return
			}
			//log.Printf("[info] before delete:%v", n.Host[strings.ToLower(mac)].Announce.Sdps)
			n.Host[strings.ToLower(mac)].Announce.Sdps = n.Host[strings.ToLower(mac)].Announce.Sdps[1:]
			//log.Printf("[info] after delete:%v", n.Host[strings.ToLower(mac)].Announce.Sdps)
		}
		n.lock.Unlock()
	}
}

func (n *Nodes) GetNodePull(macAddr string, setup int) (PullRespsonse, error) {
	n.lock.RLock()
	host, ok := n.Host[strings.ToLower(macAddr)]
	if !ok {
		return PullRespsonse{}, errors.New("no such node")
	}
	peer := host.Announce
	n.lock.RUnlock()
	nodeInfo := PullRespsonse{}
	if len(peer.Sdps) < 1 {
		return nodeInfo, errors.New(fmt.Sprintf("%s Sdps.len < 1 ", macAddr))
	}
	if time.Now().Unix()-peer.Time > peer.Timeout {
		return nodeInfo,
			errors.New(fmt.Sprintf("GetHandler.getNodeInfo %s timeout", macAddr))
	}
	nodeInfo.Setup = setup
	nodeInfo.PredictionPort = peer.PredictionPort
	nodeInfo.PeerId = peer.PeerId
	nodeInfo.Sdp = NodeOffer{
		Sdp:  peer.Sdps[0].Offer.Sdp,
		Type: peer.Sdps[0].Offer.Type,
	}
	nodeInfo.OfferId = peer.Sdps[0].OfferId
	nodeInfo.OfferId = peer.Sdps[0].RemoteOfferId
	peer.Sdps = peer.Sdps[1:]
	return nodeInfo, nil
}

func (n *Nodes) GetNodeInfo(to string) *NodeAnnounce {
	n.lock.RLock()
	log.Printf("got node announce 1:%v", to)
	resp, ok := n.Host[strings.ToLower(to)]
	n.lock.RUnlock()
	if ok {
		res := resp.Announce
		return res
	}
	return nil
}

func (n *Nodes) Run() {
	go func() {
		for {
			log.Printf("[info]:nodes run starting")
			n.lock.Lock()
			nodeCount := len(n.Host)
			log.Printf("[info]:station before cleanup current total node amount:%d", nodeCount)
			if nodeCount != 0 {
				for m, v := range n.Host {
					//log.Printf("time last:%v and timeout:%v", time.Now().Unix()-v.Announce.Time, v.Announce.Timeout)
					fmt.Println(v.Announce.PeerId)
					if time.Now().Unix()-v.Announce.Time > v.Announce.Timeout*2+60 {
						//TODO map[key] = nil
						delete(n.Host, m)
					}
				}
			nodeCount = len(n.Host)
			}
			n.lock.Unlock()
			log.Printf("[info]:station after cleanup current total node amount:%d", nodeCount)
			log.Printf("[info]:nodes run entering sleeping")
			time.Sleep(time.Second * 330)
		}
	}()
}

